/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2007 OPeNDAP, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.IO;
using System.Text;
using NDODS.DAP.Exceptions;
using NDODS.DAP.Utils;
using NDODS.DAP.XMLParser;

namespace NDODS.DAP
{
    /// <summary> An <code>Attribute</code> holds information about a single attribute in an
    /// <code>AttributeTable</code>.  It has a type, and contains either a
    /// <code>Vector</code> of <code>String</code>s containing the attribute's
    /// values, or a reference to an <code>AttributeTable</code>, if the
    /// <code>Attribute</code> is a container.  An <code>Attribute</code> may also
    /// be created as an alias pointing to another <code>Attribute</code> of any
    /// type, including container.
    /// 
    /// </summary>
    /// <author>  jehamby
    /// </author>
    /// <version>  $Revision: 15901 $
    /// </version>
    /// <seealso cref="AttributeTable">
    /// </seealso>
    [Serializable]
    public class Attribute : ICloneable
    {
        /// <summary> Returns the attribute type as a <code>String</code>.
        /// 
        /// </summary>
        /// <returns> the attribute type <code>String</code>.
        /// </returns>
        public virtual String TypeString
        {
            get
            {
                switch (type)
                {
                    case CONTAINER:
                        return "Container";

                    case ALIAS:
                        return "Alias";

                    case BYTE:
                        return "Byte";

                    case INT16:
                        return "Int16";

                    case UINT16:
                        return "UInt16";

                    case INT32:
                        return "Int32";

                    case UINT32:
                        return "UInt32";

                    case FLOAT32:
                        return "Float32";

                    case FLOAT64:
                        return "Float64";

                    case STRING:
                        return "String";

                    case URL:
                        return "Url";
                        //    case BOOLEAN: return "Boolean";

                    default:
                        return "";
                }
            }
        }

        /// <summary> Returns the attribute type constant.
        /// 
        /// </summary>
        /// <returns> the attribute type constant.
        /// </returns>
        public virtual int Type
        {
            get { return type; }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the attribute's name.
        /// 
        /// </summary>
        /// <returns> the attribute name.
        /// </returns>
        /// <summary> Returns the attribute's name.</summary>
        public virtual String Name
        {
            get { return EscapeStrings.id2www(name); }

            set { ClearName = EscapeStrings.www2id(value); }
        }

        //UPGRADE_NOTE: Respective javadoc comments were merged.  It should be changed in order to comply with .NET documentation conventions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1199'"
        /// <summary> Returns the attribute's name.
        /// 
        /// </summary>
        /// <returns> the attribute name.
        /// </returns>
        /// <summary> Returns the attribute's name.</summary>
        public virtual String ClearName
        {
            get { return name; }

            set { name = value; }
        }

        /// <summary> Returns the <code>AttributeTable</code> container.
        /// 
        /// </summary>
        /// <returns> the <code>AttributeTable</code> container, or null if not a container.
        /// </returns>
        public virtual AttributeTable ContainerN
        {
            get { return (attr is AttributeTable) ? (AttributeTable) attr : null; }
        }

        /// <summary> Returns the values of this attribute as an <code>Enumeration</code>
        /// of <code>String</code>.
        /// 
        /// </summary>
        /// <returns> an <code>Enumeration</code> of <code>String</code>.
        /// </returns>
        public virtual IEnumerator Values
        {
            get
            {
                checkVectorUsage();
                return ((ArrayList) attr).GetEnumerator();
            }
        }

        /// <summary> Returns the values of this attribute as an <code>Enumeration</code> of <code>String</code>.
        /// 
        /// </summary>
        /// <returns> an <code>Iterator<String></code> of String </code>, or null if a container..
        /// </returns>
        public virtual IEnumerator ValuesIterator
        {
            get { return (attr is ArrayList) ? ((ArrayList) attr).GetEnumerator() : null; }
        }

        internal const long serialVersionUID = 1;

        private const bool _Debug = false;
        private const bool DebugValueChecking = false;

        /// <summary> Unknown attribute type.  This is currently unused.</summary>
        public const int UNKNOWN = 0;

        /// <summary> Alias attribute type.  This is an attribute that works like a
        /// UNIX style soft link to another attribute.
        /// </summary>
        public const int ALIAS = 1;

        /// <summary> Container attribute type.  This Attribute holds an AttributeTable.</summary>
        public const int CONTAINER = 2;

        /// <summary> Byte attribute type. Holds an unsigned Byte.</summary>
        public const int BYTE = 3;

        /// <summary> Int16 attribute type.  Holds a signed Short.</summary>
        public const int INT16 = 4;

        /// <summary> UInt16 attribute type.  Holds an unsigned Short.</summary>
        public const int UINT16 = 5;

        /// <summary> Int32 attribute type.  Holds a signed Integer.</summary>
        public const int INT32 = 6;

        /// <summary> UInt32 attribute type.  Holds an unsigned Integer.</summary>
        public const int UINT32 = 7;

        /// <summary> Float32 attribute type.  Holds a Float.</summary>
        public const int FLOAT32 = 8;

        /// <summary> Float64 attribute type.  Holds a Double.</summary>
        public const int FLOAT64 = 9;

        /// <summary> String attribute type.  Holds a String.</summary>
        public const int STRING = 10;

        /// <summary> URL attribute type.  Holds a String representing a URL.</summary>
        public const int URL = 11;

        /// <summary> The type of the attribute.</summary>
        private int type;

        /// <summary> The name of the attribute.</summary>
        private String name;

        /// <summary> Either an AttributeTable or a Vector of String.</summary>
        private Object attr;

        /// <summary> Construct a container attribute.
        /// 
        /// </summary>
        /// <param name="container">the <code>AttributeTable</code> container.
        /// </param>
        /// <deprecated> Use the ctor with the name.
        /// </deprecated>
        public Attribute(AttributeTable container)
        {
            type = CONTAINER;
            attr = container;
        }

        /// <summary> Construct an <code>Attribute</code> with the given type and initial
        /// value.
        /// 
        /// </summary>
        /// <param name="type"> the type of attribute to create.  Use one of the type
        /// constants defined by this class.
        /// </param>
        /// <param name="name"> the name of the attribute.
        /// </param>
        /// <param name="value_Renamed">the initial value of this attribute.  Use the
        /// <code>appendValue</code> method to create a vector of values.
        /// </param>
        /// <param name="check">if true, check the value and throw
        /// AttributeBadValueException if it's not valid; if false do not check its
        /// validity.
        /// </param>
        /// <throws>  AttributeBadValueException thrown if the value is not a legal </throws>
        /// <summary>                                    member of type
        /// </summary>
        public Attribute(int type, String name, String value_Renamed, bool check)
        {
            if (check)
                dispatchCheckValue(type, value_Renamed);

            this.type = type;
            Name = name;
            attr = ArrayList.Synchronized(new ArrayList(10));
            ((ArrayList) attr).Add(value_Renamed);
        }

        /// <summary> Construct an <code>Attribute</code> with the given type and initial
        /// value. Checks the value of the attribute and throws an exception if
        /// it's not valid.
        /// 
        /// </summary>
        /// <param name="type"> the type of attribute to create.  Use one of the type
        /// constants defined by this class.
        /// </param>
        /// <param name="name"> the name of the attribute.
        /// </param>
        /// <param name="value_Renamed">the initial value of this attribute.  Use the
        /// <code>appendValue</code> method to create a vector of values.
        /// </param>
        /// <throws>  AttributeBadValueException thrown if the value is not a legal </throws>
        /// <summary>                                    member of type
        /// </summary>
        public Attribute(int type, String name, String value_Renamed)
        {
            dispatchCheckValue(type, value_Renamed);

            this.type = type;
            Name = name;
            attr = ArrayList.Synchronized(new ArrayList(10));
            ((ArrayList) attr).Add(value_Renamed);
        }

        /// <summary> Construct a container attribute.
        /// 
        /// </summary>
        /// <param name="container">the <code>AttributeTable</code> container.
        /// </param>
        public Attribute(String name, AttributeTable container)
        {
            type = CONTAINER;
            Name = name;
            attr = container;
        }


        /// <summary> Construct an empty attribute with the given type.
        /// 
        /// </summary>
        /// <param name="type">the type of attribute to create.  Use one of the type
        /// constants defined by this class, other than <code>CONTAINER</code>.
        /// </param>
        /// <throws>  IllegalArgumentException thrown if </throws>
        /// <summary>                                  <code>type</code> is <code>CONTAINER</code>. To construct an empty
        /// container attribute, first construct and empty AttributeTable and then
        /// use that to construct the Attribute.
        /// </summary>
        public Attribute(String name, int type)
        {
            this.type = type;
            Name = name;
            if (type == CONTAINER)
                throw new ArgumentException("Can't construct an Attribute(CONTAINER)");
            else
                attr = ArrayList.Synchronized(new ArrayList(10));
        }

        /// <summary> Returns a clone of this <code>Attribute</code>.  A deep copy is performed
        /// on all attribute values.
        /// 
        /// </summary>
        /// <returns> a clone of this <code>Attribute</code>.
        /// </returns>
        public virtual Object Clone()
        {
            try
            {
                Attribute a = (Attribute) base.MemberwiseClone();
                a.name = name;
                // assume type, is_alias, and aliased_to have been cloned already
                if (type == CONTAINER)
                    a.attr = ((AttributeTable) attr).Clone();
                else
                    a.attr = ((ArrayList) attr).Clone();
                return a;
            }
                //UPGRADE_NOTE: Exception 'java.lang.CloneNotSupportedException' was converted to 'System.Exception' which has different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1100'"
            catch (Exception e)
            {
                // this shouldn't happen, since we are Cloneable
                throw new ApplicationException();
            }
        }

        /// <summary> Returns the attribute type as a <code>String</code>.
        /// 
        /// </summary>
        /// <returns> the attribute type <code>String</code>.
        /// </returns>
        public static int getTypeVal(String s)
        {
            if (s.ToUpper().Equals("Container".ToUpper()))
                return CONTAINER;
            else if (s.ToUpper().Equals("Byte".ToUpper()))
                return BYTE;
            else if (s.ToUpper().Equals("Int16".ToUpper()))
                return INT16;
            else if (s.ToUpper().Equals("UInt16".ToUpper()))
                return UINT16;
            else if (s.ToUpper().Equals("Int32".ToUpper()))
                return INT32;
            else if (s.ToUpper().Equals("UInt32".ToUpper()))
                return UINT32;
            else if (s.ToUpper().Equals("Float32".ToUpper()))
                return FLOAT32;
            else if (s.ToUpper().Equals("Float64".ToUpper()))
                return FLOAT64;
            else if (s.ToUpper().Equals("String".ToUpper()))
                return STRING;
            else if (s.ToUpper().Equals("URL".ToUpper()))
                return URL;
            else
                return UNKNOWN;
        }

        /// <summary> Returns true if the attribute is a container.
        /// 
        /// </summary>
        /// <returns> true if the attribute is a container.
        /// </returns>
        public virtual bool isContainer()
        {
            return (type == CONTAINER);
        }

        /// <summary> Returns true if the attribute is an alias.
        /// 
        /// </summary>
        /// <returns> true if the attribute is an alias.
        /// </returns>
        public virtual bool isAlias()
        {
            return (false);
        }


        /// <summary> Returns the <code>AttributeTable</code> container.
        /// 
        /// </summary>
        /// <returns> the <code>AttributeTable</code> container.
        /// </returns>
        public virtual AttributeTable getContainer()
        {
            checkContainerUsage();
            return (AttributeTable) attr;
        }

        /// <summary> Returns the attribute value at <code>index</code>.
        /// 
        /// </summary>
        /// <param name="index">the index of the attribute value to return.
        /// </param>
        /// <returns> the attribute <code>String</code> at <code>index</code>.
        /// </returns>
        public virtual String getValueAt(int index)
        {
            checkVectorUsage();
            return (String) ((ArrayList) attr)[index];
        }

        /// <summary> Returns the attribute value at <code>index</code>.</summary>
        /// <param name="index">the index of the attribute value to return.
        /// </param>
        /// <returns> the attribute <code>String</code> at <code>index</code>, or null if a container..
        /// </returns>
        public virtual String getValueAtN(int index)
        {
            if (!(attr is ArrayList))
                return null;
            return (String) ((ArrayList) attr)[index];
        }


        /// <summary> Append a value to this attribute. Always checks the validity of the
        /// attribute's value.
        /// 
        /// </summary>
        /// <param name="value">the attribute <code>String</code> to add.
        /// </param>
        /// <throws>  AttributeBadValueException thrown if the value is not a legal </throws>
        /// <summary>                                    member of type
        /// </summary>
        public virtual void appendValue(String value_Renamed)
        {
            checkVectorUsage();
            appendValue(value_Renamed, true);
        }

        /// <summary> Append a value to this attribute.
        /// 
        /// </summary>
        /// <param name="value">the attribute <code>String</code> to add.
        /// </param>
        /// <param name="check">if true, check the validity of he attribute's value, if
        /// false don't.
        /// </param>
        /// <throws>  AttributeBadValueException thrown if the value is not a legal </throws>
        /// <summary>                                    member of type
        /// </summary>
        public virtual void appendValue(String value_Renamed, bool check)
        {
            checkVectorUsage();
            if (check)
                dispatchCheckValue(type, value_Renamed);

            ((ArrayList) attr).Add(value_Renamed);
        }

        /// <summary> Remove the <code>i</code>'th <code>String</code> from this attribute.
        /// 
        /// </summary>
        /// <param name="index">the index of the value to remove.
        /// </param>
        public virtual void deleteValueAt(int index)
        {
            checkVectorUsage();
            ((ArrayList) attr).RemoveAt(index);
        }

        /// <summary> Check if the value is legal for a given type.
        /// 
        /// </summary>
        /// <param name="type"> the type of the value.
        /// </param>
        /// <param name="value">the value <code>String</code>.
        /// </param>
        /// <throws>  AttributeBadValueException if the value is not a legal </throws>
        /// <summary>                                    member of type
        /// </summary>
        private static void dispatchCheckValue(int type, String value_Renamed)
        {
            switch (type)
            {
                case BYTE:
                    if (!checkByte(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not a Byte value.");
                    break;


                case INT16:
                    if (!checkShort(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not an Int16 value.");
                    break;


                case UINT16:
                    if (!checkUShort(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not an UInt16 value.");
                    break;


                case INT32:
                    if (!checkInt(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not an Int32 value.");
                    break;


                case UINT32:
                    if (!checkUInt(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not an UInt32 value.");
                    break;


                case FLOAT32:
                    if (!checkFloat(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not a Float32 value.");
                    break;


                case FLOAT64:
                    if (!checkDouble(value_Renamed))
                        throw new AttributeBadValueException("`" + value_Renamed + "' is not a Float64 value.");
                    break;

                    //    case BOOLEAN:
                    //      if(!checkBoolean(value))
                    //	throw new AttributeBadValueException("`" + value + "' is not a Boolean value.");
                    //      break;


                default:
                    // Assume UNKNOWN, CONTAINER, STRING, and URL are okay.
                    break;
            }
        }


        /// <summary> Check if string is a valid Byte.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkByte(String s)
        {
            try
            {
                // Byte.parseByte() can't be used because values > 127 are allowed
                short val = Int16.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkByte() - string: '" + s + "'   value: " + val);
                if (val > 0xFF || val < 0)
                    return false;
                else
                    return true;
            }
            catch (FormatException e)
            {
                throw new AttributeBadValueException("`" + s + "' is not a Byte value.");
            }
        }

        /// <summary> Check if string is a valid Int16.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkShort(String s)
        {
            try
            {
                short val = Int16.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkShort() - string: '" + s + "'   value: " + val);
                return true;
            }
            catch (FormatException e)
            {
                return false;
            }
        }

        /// <summary> Check if string is a valid UInt16.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkUShort(String s)
        {
            // Note: Because there is no Unsigned class in Java, use Long instead.
            try
            {
                long val = Int64.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkUShort() - string: '" + s + "'   value: " + val);
                if (val > 0xFFFFL)
                    return false;
                else
                    return true;
            }
            catch (FormatException e)
            {
                return false;
            }
        }

        /// <summary> Check if string is a valid Int32.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkInt(String s)
        {
            try
            {
                int val = Int32.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkInt() - string: '" + s + "'   value: " + val);
                return true;
            }
            catch (FormatException e)
            {
                return false;
            }
        }

        /// <summary> Check if string is a valid UInt32.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkUInt(String s)
        {
            // Note: Because there is no Unsigned class in Java, use Long instead.
            try
            {
                long val = Int64.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkUInt() - string: '" + s + "'   value: " + val);
                if (val > 0xFFFFFFFFL)
                    return false;
                else
                    return true;
            }
            catch (FormatException e)
            {
                return false;
            }
        }

        /// <summary> Check if string is a valid Float32.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkFloat(String s)
        {
            try
            {
                float val = Single.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkFloat() - string: '" + s + "'   value: " + val);
                return true;
            }
            catch (FormatException e)
            {
                if (s.ToUpper().Equals("nan".ToUpper()) || s.ToUpper().Equals("inf".ToUpper()))
                    return true;

                return false;
            }
        }

        /// <summary> Check if string is a valid Float64.
        /// 
        /// </summary>
        /// <param name="s">the <code>String</code> to check.
        /// </param>
        /// <returns> true if the value is legal.
        /// </returns>
        private static bool checkDouble(String s)
        {
            try
            {
                double val = Double.Parse(s);
                if (DebugValueChecking)
                    Console.Out.WriteLine("Attribute.checkDouble() - string: '" + s + "'   value: " + val);
                return true;
            }
            catch (FormatException e)
            {
                if (s.ToUpper().Equals("nan".ToUpper()) || s.ToUpper().Equals("inf".ToUpper()))
                    return true;

                return false;
            }
        }

        /* NOTE: THis method is flawed think of a better way to do this
		BEFORE you uncomment it!! The valueOf() method for
		Boolean returns true if and only if the string passed
		is equal to (ignoring case) to "true"
		Otherwise it returns false... A lousy test., UNLESS
		the JAVA implementation of a Boolean string works for
		you.
		
		* Check if string is a valid Boolean.
		* @param s the <code>String</code> to check.
		* @return true if the value is legal.
		
		private static final bool checkBoolean(String s) {
		try {
		Boolean.valueOf(s);
		return true;
		}
		catch (NumberFormatException e) {
		return false;
		}
		}
		*/


        private void checkVectorUsage()
        {
            if (!(attr is ArrayList))
            {
                throw new NoSuchAttributeException("The Attribute '" + Name + "' is a container. " +
                                                   "It's contents are Attribues, not values.");
            }
        }

        private void checkContainerUsage()
        {
            if (_Debug)
                Console.Out.Write("Attribute.checkContainerUsage(): ");

            if (!(attr is AttributeTable))
            {
                throw new NoSuchAttributeException("The Attribute '" + Name + "' is not a container (AttributeTable)." +
                                                   "It's content is made up of values, not other Attributes.");
            }
            if (_Debug)
                Console.Out.WriteLine("The Attribute is a container");
        }


        public virtual void print(StreamWriter os, String pad)
        {
            if (_Debug)
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                os.WriteLine("Entered Attribute.Print()");
            }

            if (this.attr is AttributeTable)
            {
                if (_Debug)
                {
                    //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                    os.WriteLine("  Attribute \"" + name + "\" is a Container.");
                }

                ((AttributeTable) this.attr).print(os, pad);
            }
            else
            {
                if (_Debug)
                {
                    //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                    os.WriteLine("    Printing Attribute \"" + name + "\".");
                }

                os.Write(pad + TypeString + " " + Name + " ");

                IEnumerator es = ((ArrayList) this.attr).GetEnumerator();


                //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'"
                while (es.MoveNext())
                {
                    //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'"
                    String val = (String) es.Current;
                    bool useQuotes = false;

                    if (val.IndexOf(' ') >= 0 || val.IndexOf('\t') >= 0 || val.IndexOf('\n') >= 0 ||
                        val.IndexOf('\r') >= 0)
                    {
                        if (val.IndexOf('\"') != 0)
                            useQuotes = true;
                    }

                    if (useQuotes)
                        os.Write("\"" + val + "\"");
                    else
                        os.Write(val);
                    //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'"
                    if (es.MoveNext())
                        os.Write(", ");
                }
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                os.WriteLine(";");
            }
            if (_Debug)
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                os.WriteLine("Leaving Attribute.Print()");
            }
            os.Flush();
        }

        /// <summary> Print the attribute on the given <code>OutputStream</code>.
        /// 
        /// </summary>
        /// <param name="os"> the <code>OutputStream</code> to use for output.
        /// </param>
        /// <param name="pad">the number of spaces to indent each line.
        /// </param>
        public void print(Stream os, String pad)
        {
            //UPGRADE_WARNING: At least one expression was used more than once in the target code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1181'"
            print(
                new StreamWriter(
                    new StreamWriter(new StreamWriter(os, Encoding.Default).BaseStream,
                                     new StreamWriter(os, Encoding.Default).Encoding).BaseStream,
                    new StreamWriter(new StreamWriter(os, Encoding.Default).BaseStream,
                                     new StreamWriter(os, Encoding.Default).Encoding).Encoding), pad);
        }

        /// <summary> Print the attribute on the given <code>StreamWriter</code> with
        /// four spaces of indentation.
        /// 
        /// </summary>
        /// <param name="os">the <code>StreamWriter</code> to use for output.
        /// </param>
        public void print(StreamWriter os)
        {
            print(os, "");
        }

        /// <summary> Print the attribute on the given <code>OutputStream</code> with
        /// four spaces of indentation.
        /// 
        /// </summary>
        /// <param name="os">the <code>OutputStream</code> to use for output.
        /// </param>
        public void print(Stream os)
        {
            print(os, "");
        }


        /// <param name="os">
        /// </param>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual void printXML(Stream os)
        {
            printXML(os, "");
        }


        /// <param name="os">
        /// </param>
        /// <param name="pad">
        /// </param>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual void printXML(Stream os, String pad)
        {
            //UPGRADE_WARNING: At least one expression was used more than once in the target code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1181'"
            StreamWriter pw =
                new StreamWriter(
                    new StreamWriter(new StreamWriter(os, Encoding.Default).BaseStream,
                                     new StreamWriter(os, Encoding.Default).Encoding).BaseStream,
                    new StreamWriter(new StreamWriter(os, Encoding.Default).BaseStream,
                                     new StreamWriter(os, Encoding.Default).Encoding).Encoding);
            printXML(pw, pad);
            pw.Flush();
        }


        /// <param name="pw">
        /// </param>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual void printXML(StreamWriter pw)
        {
            printXML(pw, "");
        }


        /// <param name="pw">
        /// </param>
        /// <param name="pad">
        /// </param>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual void printXML(StreamWriter pw, String pad)
        {
            printXML(pw, pad, false);
        }


        /// <param name="pw">
        /// </param>
        /// <param name="pad">
        /// </param>
        /// <param name="constrained">
        /// </param>
        /// <opendap.ddx.experimental>  </opendap.ddx.experimental>
        public virtual void printXML(StreamWriter pw, String pad, bool constrained)
        {
            if (_Debug)
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                pw.WriteLine("Entered Attribute.PrintXML()");
            }

            if (this.attr is AttributeTable)
            {
                if (_Debug)
                {
                    //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                    pw.WriteLine("  Attribute \"" + name + "\" is a Container.");
                }

                ((AttributeTable) this.attr).printXML(pw, pad, constrained);
            }
            else
            {
                if (_Debug)
                {
                    //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                    pw.WriteLine("    Printing Attribute \"" + name + "\".");
                }

                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                pw.WriteLine(pad + "<Attribute name=\"" + DDSXMLParser.normalizeToXML(Name) + "\" type=\"" + TypeString +
                             "\">");

                IEnumerator es = ((ArrayList) this.attr).GetEnumerator();
                //UPGRADE_TODO: Method 'java.util.Enumeration.hasMoreElements' was converted to 'System.Collections.IEnumerator.MoveNext' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationhasMoreElements'"
                while (es.MoveNext())
                {
                    //UPGRADE_TODO: Method 'java.util.Enumeration.nextElement' was converted to 'System.Collections.IEnumerator.Current' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javautilEnumerationnextElement'"
                    String val = (String) es.Current;
                    //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                    pw.WriteLine(pad + "\t" + "<value>" + DDSXMLParser.normalizeToXML(val) + "</value>");
                }
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                pw.WriteLine(pad + "</Attribute>");
            }
            if (_Debug)
            {
                //UPGRADE_TODO: Method 'java.io.StreamWriter.println' was converted to 'System.IO.TextWriter.WriteLine' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioStreamWriterprintln_javalangString'"
                pw.WriteLine("Leaving Attribute.Print()");
            }
            pw.Flush();
        }
    }

    // $Log: Attribute.java,v $
    // Revision 1.2  2003/09/02 17:49:34  ndp
    // *** empty log message ***
    //
    // Revision 1.1  2003/08/12 23:51:25  ndp
    // Mass check in to begin Java-OPeNDAP development work
    //
    // Revision 1.10  2003/04/16 21:50:53  caron
    // turn off debug flag
    //
    // Revision 1.9  2003/04/07 22:12:32  jchamber
    // added serialization
    //
    // Revision 1.8  2003/02/12 16:41:15  ndp
    // *** empty log message ***
    //
    // Revision 1.7  2002/10/08 21:59:18  ndp
    // Added XML functionality to the core. This includes the new DDS code (aka DDX)
    // for parsing XML representations of the dataset description ( that's a DDX)
    // Also BaseType has been modified to hold Attributes and methods added to DDS
    // to ingest DAS's (inorder to add Attributes to variables) and to get the DAS
    // object from the DDS. Geturl and DConnect hav been modified to provide client
    // access to this new set of functionalites. ndp 10/8/2002
    //
    // Revision 1.6  2002/08/27 04:30:11  ndp
    // AttributeTable added to BaseType
    // Interfaces for AttributeTable implmented in BaseType, DConstructor, and DDS
    // Methods added to DDS to Print DAS and to return a DAS object.
    // XMLParser updated to populate BaseType AttributeTables.
    //
    // Revision 1.5  2002/05/30 23:24:58  jimg
    // I added methods that provide a way to add attribute values without checking
    // their type/value validity. This provides a way to add bad values in a
    // *_dods_error attribute container so that attributes that are screwed up won't
    // be lost (because they might make sense to a person, for example) or cause the
    // whole DAS to break. The DAS parser (DASParser.jj) uses this new code.
    //
}